poetry でアプリケーションをリリースしてみよう
poetry について
Python には多数の便利な拡張モジュールがあり、多くのモジュールやアプリケーションがこれらに依存しています。これがために、Pythonにはスタンドアロンの実行可能ファイルを簡単に構築するうまい方法がありません。
例えば、Java では .jarファイル、Goでは実行可能ファイル、というように、依存関係やランタイム情報を単一の配布可能なファイルにまとめる標準化された方法はPythonにはありません。
Python では、コマンドラインツールやライブラリをパッケージングしてPyPIで公開し、pip コマンドでインストールすることで対応してきました。まだ拡張モジュールなど実行環境に依存しますが、ーザー側の視点ではパッケージは独立したものと扱うことができます。
このパッケージングについては、試行錯誤と変化が激しいものでした(参考資料3)。難しそうに思えますが、poetry を使ったパッケージの構築は、実はとても簡単です。
ここでは、Poetry ツールを使ったパッケージ構築の方法を説明します。
poetry をインストール
はじめに poetry をインストールしておきます。
code: pip
$ pip install poetry
公式に推奨されているインストール方法は次のものです。
code: bash
pip でインストールする場合とでは、次のような内容が違います。
インストール先が Lib/site-packages/以外にすることができる
環境変数POETRY_HOMEで指示、デフォルトはユーザのホームディレクトリ
poetry 自身のバージョンアップを poetry で行える
code: bash
$ poetry self update
poetry を使い始めると基本的には pip コマンドを使うことがなくなるため、poetry で完結したい人にはおすすめです。
インストールが終わったら、poetry とコマンドを実行してみましょう。
ヘルプメッセージが表示されれば問題なく動作できます。
code: zsh
(poetry) % poetry
Poetry version 1.1.8
USAGE
ARGUMENTS
<command> The command to execute
<arg> The arguments of the command
GLOBAL OPTIONS
-h (--help) Display this help message
-q (--quiet) Do not output any message
-v (--verbose) Increase the verbosity of messages: "-v" for normal
output, "-vv" for more verbose output and "-vvv" for
debug
-V (--version) Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n (--no-interaction) Do not ask any interactive question
AVAILABLE COMMANDS
about Shows information about Poetry.
add Adds a new dependency to pyproject.toml.
build Builds a package, as a tarball and a wheel by default.
cache Interact with Poetry's cache
check Checks the validity of the
pyproject.toml file.
config Manages configuration settings.
debug Debug various elements of Poetry.
env Interact with Poetry's project environments.
export Exports the lock file to alternative formats.
help Display the manual of a command
init Creates a basic pyproject.toml file in the
current directory.
install Installs the project dependencies.
lock Locks the project dependencies.
new Creates a new Python project at <path>.
publish Publishes a package to a remote repository.
remove Removes a package from the project dependencies.
run Runs a command in the appropriate environment.
search Searches for packages on remote repositories.
self Interact with Poetry directly.
shell Spawns a shell within the virtual environment.
show Shows information about packages.
update Update the dependencies as according to the
pyproject.toml file.
version Shows the version of the project or bumps it when a
valid bump rule is provided.
タブ補完を有効にする
poetry は、Bash、Fish、Zsh 用の補完スクリプトの生成をサポートしています。詳細は poetry help completions を参照してくださいが、要点は以下のいずれかを使用するだけです。
code: bash
$ poetry completions bash > /etc/bash_completion.d/poetry.bash-completion
code: homebew bash
$ poetry completions bash > $(brew --prefix)/etc/bash_completion.d/poetry.bash-completion
code: Fish
poetry completions fish > ~/.config/fish/completions/poetry.fish
code: homeebew Fish
$ poetry completions fish > (brew --prefix)/share/fish/vendor_completions.d/poetry.fish
code: zsh
% poetry completions zsh > ~/.zfunc/_poetry
code: homebew zsh
% poetry completions zsh > $(brew --prefix)/share/zsh/site-functions/_poetry
code: custom zsh
% mkdir $ZSH_CUSTOM/plugins/poetry
% poetry completions zsh > $ZSH_CUSTOM/plugins/poetry/_poetry
注意: 変更を有効にするために、シェルを再起動する必要があるかもしれません。
zshの場合は、compinitの前に~/.zshrc に以下の行を追加する必要があります(homebrew setupではありません)。
code: ~/.zshrc
fpath+=~/.zfunc
poetryの設定
poetry config でportry自身の設定を確認/再設定することができます。
code: bash
(poetry) % poetry config --list
cache-dir = "/Users/XXXXX/Library/Caches/pypoetry"
experimental.new-installer = true
installer.parallel = true
virtualenvs.create = true
virtualenvs.in-project = true
virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/XXXXX/Library/Caches/pypoetry/virtualenvs
cache-dir: Poetryが使うキャッシュディレクトリのpathです。defaultは以下のとおり。
mac: ~/Library/Caches/pypoetry
Linux: ~/.cache/pypoetry
virtualenvs.create:まだ仮想環境が存在していない場合、新しい仮想環境を作成するか。defaultはtrue
virtualenvs.in-project:プロジェクトのルートディレクトリに仮想環境を作成するか。defaultはtrue
仮想環境の消し忘れがありえると思うので、この設定はtrueにしておくべきです
virtualenvs.path: 仮想環境が作成されるディレクトリ。defaultは {cache-dir}/virtualenvs
repositories.{name}: PyPI以外のプライベートリポジトリを利用したい場合などに設定。
設定を変更する場合は、次のようにグローバル、ローカルと区別することができまうs.
code: COMMAND
# グローバルの設定
poetry config {項目名} {値}
# プロジェクトローカルの設定
poetry config {項目名} {値} --local
プロジェクトの作成
poetry new でプロジェクトを作成します。
はじめに使用方法を確認しておきましょう。
code: zsh
(poetry) % poetry new --help
USAGE
ARGUMENTS
<path> The path to create the project at.
OPTIONS
--name Set the resulting package name.
--src Use the src layout for the project.
GLOBAL OPTIONS
-h (--help) Display this help message
-q (--quiet) Do not output any message
-v (--verbose) Increase the verbosity of messages: "-v" for normal
output, "-vv" for more verbose output and "-vvv" for
debug
-V (--version) Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n (--no-interaction) Do not ask any interactive question
それぞれのサブコマンドで--helpオプションが使用できます。
まず、poertry new に続けてプロジェクト名与えて実行します。
code: zsh
(poetry) % poetry new mymath
Created package mymath in mymath
デフォルトではプロジェクト名がカラー表示されます。
モノクロームで出力させたいときは --no-ansi のオプションを与えます。
プロジェクト名のディレクトリが作成されます。
code: zsh
(poetry) $ tree mymath
mymath
├── README.rst
├── mymath
│ └── __init__.py
├── pyproject.toml
└── tests
├── __init__.py
└── test_mymath.py
2 directories, 5 files
ファイルの確認
code: zsh
(poetry) $ cd mymath
(poetry) $ ls
README.rst mymath pyproject.toml tests
README.rst は reStructuredText フォーマットという簡単なルールで記述するファイルです。いま時点ではこのままで構いません。
pyproject.toml はこの時点で次のような内容になっています。(メールアドレスについては加工しています)
code: pyproject.toml
name = "mymath"
version = "0.1.0"
description = ""
python = "^3.9"
pytest = "^5.2"
build-backend = "poetry.core.masonry.api"
ここでは4つのセクションが定義されています。(詳細は、PEP-518 を参照) [tool.poetry]: 名前やバージョンなど、依存パッケージに関連しないすべてのメタデータを定義しています。
[tool.poetry.dependencies]: プロジェクトに必要なすべての依存関係を定義しています。
[tool.poetry.dev-dependencies]: プロジェクトをビルドするためではなく、テスト、ビルド、ドキュメンテーションなどの他のアクションを実行するために必要な、すべての開発時での依存関係を定義します。
[build-system]: PEP 517 に示されているように、ビルドシステムを定義します。 poetryがプロジェクトを初期化するのが気に入らない場合や、既にpoetryで制御したいプロジェクトがある場合は、initコマンドを使うことができます。プロジェクトを設定するためのいくつかの質問にインタラクティブに回答してゆきます。
code: bash
(poetry) $ poetry init
This command will guide you through creating your pyproject.toml config.
Description []: typer demo
License []: MIT
Compatible Python versions ^3.9: (以下略)
プロジェクトをインストール
pyproject.toml の定義にしたがって、依存関係を解決してインストールします。
code: bash
(poetry) $ poetry install
Updating dependencies
Resolving dependencies... (1.6s)
Writing lock file
Package operations: 6 installs, 1 update, 0 removals
• Installing attrs (21.2.0)
• Installing more-itertools (8.12.0)
• Updating packaging (20.9 -> 21.3)
• Installing pluggy (0.13.1)
• Installing py (1.11.0)
• Installing wcwidth (0.2.5)
• Installing pytest (5.4.3)
Installing the current project: mymath (0.1.0)
パッケージの追加
poetry add でプロジェクトにパッケージを追加することができます。ここで指定したパッケージは依存関係として pyproject.toml に追記されます。
では、 typer モジュールを追加してみましょう。add は、自動的に適切なバージョンを見つけ、指定したパッケージとそれに依存関係にあるパッケージをインストールします。
code: bash
(poetry) $ poetry add typer
Using version ^0.4.0 for typer
Updating dependencies
Resolving dependencies... (0.3s)
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing click (8.0.3)
• Installing typer (0.4.0)
開発用の依存関係、つまりpytestのようにプロジェクトに直接関係のないものをインストールしたい場合は、-Dオプションを指定します。
code: bash
(poetry) $ poetry add -D pytest
addでパッケージを追加するとき、既にインストールされているパッケージを追加しようとすると、エラーになるときがあります。
code: bash
% poetry add -D pytest
The following packages are already present in the pyproject.toml and will be skipped:
• pytest
If you want to update it to the latest compatible version, you can use poetry update package.
If you prefer to upgrade it to the latest available version, you can use poetry add package@latest.
Nothing to add.
パッケージを追加する add により依存関係を記述した poetry.lock が作成されます。
今は、この内容については特に触れません。
追加したパッケージの確認
poetry show で追加したパッケージとバージョンを確認することができます。
code: bash
% poetry show
attrs 21.2.0 Classes Without Boilerplate
click 8.0.3 Composable command line interface toolkit
more-itertools 8.12.0 More routines for operating on iterables, beyond iter...
packaging 21.3 Core utilities for Python packages
pluggy 0.13.1 plugin and hook calling mechanisms for python
py 1.11.0 library with cross-python path, ini-parsing, io, code...
pyparsing 3.0.6 Python parsing module
pytest 5.4.3 pytest: simple powerful testing with Python
typer 0.4.0 Typer, build great CLIs. Easy to code. Based on Pytho...
wcwidth 0.2.5 Measures the displayed width of unicode strings in a ...
code: bash
$ poetry show --tree
pytest 5.4.3 pytest: simple powerful testing with Python
├── atomicwrites >=1.0
├── attrs >=17.4.0
├── colorama *
├── more-itertools >=4.0.0
├── packaging *
│ └── pyparsing >=2.0.2,<3.0.5 || >3.0.5
├── pluggy >=0.12,<1.0
├── py >=1.5.0
└── wcwidth *
typer 0.4.0 Typer, build great CLIs. Easy to code. Based on Python type hints.
└── click >=7.1.1,<9.0.0
└── colorama *
パッケージの更新
この場合、pytest を最新のバージョン 6.2.5 にしたいときは、pyproject.toml と poetry.lock を次のように修正します。
code: 抜粋 pyproject.toml
# pytest = "^5.2"
pytest = "^6.2"
code: 抜粋 poetry.lock
package
name = "pytest"
# version = "5.4.3"
version = "6.2.5"
このあと、 poetry update を実行すると更新することができます。
code: bash
% poetry update
Updating dependencies
Resolving dependencies... (2.0s)
Writing lock file
Package operations: 2 installs, 2 updates, 2 removals
• Removing more-itertools (8.12.0)
• Removing wcwidth (0.2.5)
• Installing iniconfig (1.1.1)
• Updating pluggy (0.13.1 -> 1.0.0)
• Installing toml (0.10.2)
• Updating pytest (5.4.3 -> 6.2.5)
code: bash
$ poetry show --tree
pytest 6.2.5 pytest: simple powerful testing with Python
├── atomicwrites >=1.0
├── attrs >=19.2.0
├── colorama *
├── iniconfig *
├── packaging *
│ └── pyparsing >=2.0.2,<3.0.5 || >3.0.5
├── pluggy >=0.12,<2.0
├── py >=1.8.2
└── toml *
typer 0.4.0 Typer, build great CLIs. Easy to code. Based on Python type hints.
└── click >=7.1.1,<9.0.0
└── colorama *
コードを追加
はじめは typer_tutorial/__init__.py は次のような内容になっています。
code: python typer_tutorial/__init__.py
__version__ = '0.1.0'
まず、typer_tutorial ディレクトリ以下にコマンドを作成しましょう。
code: typer_tutorial/demo.py
import typer
app = typer.Typer(add_completion=False)
@app.command()
def main(
name: str = typer.Option("World",
help="The name to say hi to."
)
):
typer.echo(f"Hello {name}")
if __name__ == "__main__":
app()
コードの実行
このコードを python で実行してみます。
code: bash
$ python typer_tutorial/demo.py --help
Options:
--help Show this message and exit.
$ python typer_tutorial/demo.py --name Jack
Hello Jack
今度は、 poetry から実行してみましょう。
code: bash
$ poetry run python typer_tutorial/demo.py --name=Jack
Hello Jack
パッケージをインストール
編集可能モード
pip では-e オプションを与えることで、編集可能モードでインストールすることができます。
code: bash
$ pip install -e /path/to/your/project
この方法でインストールすると、コードの変更がすぐに反映され、再パッケージ化の必要がなくなります。
まず、理解いするべきことは、PEP 517 は編集可能モードでのインストールをサポートしていないことです。poetry では、メインプロジェクトについては編集可能モードでインストールされます。 依存関係のみをインストールしたい場合は、--no-rootフラグを付けてinstallコマンドを実行してください。
code: bash
$ poetry install --no-root
依存ライブラリについては、poetry add --ediable で編集可能モードでインストールすることができます。
また、同じ仮想環境内で、いくつかの依存関係をメインプロジェクトと一緒に編集可能な状態でインストールすることも可能です。例えば、以下のようなものです。
code: 抜粋 pyproject.toml
mypackage = { path = "/path/to/mypackage/", develop = true }
要件ファイルの生成
poetry では使用する必要がありませんが、要件ファイル requirements.txt が必要になるある場合があります。この場合は、次のコマンドでrequirements.txt を生成することができます。
code: bash
$ poetry export --without-hashes -f requirements.txt -o requirements.txt
--without-hashes オプションを与えなくても問題はりませんが、可読性は低くなります。
参考
5. poetry プロジェクトのテンプレート
6. Faouzi BRAZA氏の Modern Python シリーズ